﻿using System.Collections.Generic;
using System;
namespace gemGenerator
{
    public interface CoordinateTransform
    {
        //from XYZ to polar, necessary for girdle
        VectorD Inverse(VectorD v);

        //from polar to XYZ
        VectorD Transform(VectorD v);
        
        bool SetScale(double x, double y);

        //optional parameters
        bool SetParam(double x, double y);
    }
    public class PolarTransform : CoordinateTransform
    {
        double scaleX = 1, scaleY = 1;

        public VectorD Transform(VectorD v)
        { 
            return new VectorD(scaleX * v.X * Math.Cos(v.Y), scaleY * v.X * Math.Sin(v.Y), v.Z); 
        }
        public VectorD Inverse(VectorD v)
        {
            double x = v.X / scaleX;
            double y = v.Y / scaleY;
            double r = Math.Sqrt(x*x + y*y);
            double fi = r>1e-9? Math.Acos(x / r):0;
            if(y < 0) fi = (2*Math.PI)-fi;
            return new VectorD(r, fi, v.Z);
        }

        public bool SetScale(double x, double y)
        {
            if (x > 0 && y > 0)
            {
                scaleX = x; scaleY = y;
                return true;
            }
            return false;
        }
        public bool SetParam(double x, double y)
        {
            return false;
        }

    }

    public class CushionTransform : CoordinateTransform
    {
        double scaleX = 1, scaleY = 1;
        double divisor = 1.0/6.0;
     
        public VectorD Transform(VectorD v)
        {
            double r = v.X, phi = v.Y; 
            double s = Math.Sin(2 * phi);
            r += (r * s * s * divisor);
           
            return new VectorD(scaleX * r * Math.Cos(phi), scaleY * r * Math.Sin(phi), v.Z); 
        }
        public VectorD Inverse(VectorD v)
        {
            double x = v.X / scaleX;
            double y = v.Y / scaleY; 
            double r = Math.Sqrt(x * x + y * y);
            double phi = Math.Acos(x / r);
            if(y < 0) phi = (2*Math.PI)-phi;
            
            double s = Math.Sin(2 * phi);
            r -= (r * s * s * divisor);
            return new VectorD(r, phi, v.Z);
        }
        public bool SetScale(double x, double y)
        {
            if (x > 0 && y > 0)
            {
                scaleX = x; scaleY = y;
                return true;
            }
            return false;
        }
        public bool SetParam(double x, double y)
        {
            if (x == 0) return false;
            divisor = 1 / x;
            return true;
        }

    }
    public class RectangleTransform : CoordinateTransform
    {
        
        double scaleX = 1, scaleY = 1;
        double spaceX=0, spaceY=0;
        const double inner = 0.01; //coords in this range get stretched, higher coordinates get moved
        public VectorD Transform(VectorD v)
        {
            
            double cos = Math.Cos(v.Y);
            double sin = Math.Sin(v.Y);

            double x = v.X * cos;
            double y = v.X * sin;

            if (v.X < 1e-5) x = y = 0;
            else
            {
                if (x > inner) x += spaceX - inner;
                else if (x < -inner) x -= spaceX - inner;
                else x *= spaceX / inner;

                if (y > inner) y += spaceY - inner;
                else if (y < -inner) y -= spaceY - inner;
                else y *= spaceY / inner;
            }
            return new VectorD(scaleX * x, scaleY * y, v.Z);
        }
        public VectorD Inverse(VectorD v)
        {
            double x = v.X / scaleX;
            double y = v.Y / scaleY;

            if (x < -spaceX)
                x += spaceX - inner;
            else if (x > spaceX)
                x -= spaceX - inner;
            else
                if (x != 0) x *= inner / spaceX;
            
            if (y < -spaceY)
                y += spaceX - inner;
            else if (y > spaceY)
                y -= spaceY - inner;
            else
                if (y != 0) y *= inner / spaceY;

            
            double r = Math.Sqrt(x * x + y * y);
            double fi = Math.Acos(x / r);
            if (y < 0) fi = (2 * Math.PI) - fi;
            return new VectorD(r, fi, v.Z);
        }

        public bool SetScale(double x, double y)
        {
            if (x > 0 && y > 0)
            {
                scaleX = x; scaleY = y;
                return true;
            }
            return false;
        }
        public bool SetParam(double x, double y)
        {
            if (x >= 0 && y >= 0 && x <= 5 && y <= 5)
            {
                spaceX = x; spaceY = y;
                return true;
            }
            return false;
        }

    }
    public class PearTransform : CoordinateTransform
    {
        double scaleX = 1, scaleY = 1;
        double exp = 6;
        double div = 1;
        public VectorD Transform(VectorD v)
        {
            double r = v.X;
            double phi = v.Y;
            const double pi = Math.PI;
            r *= 1 + Math.Pow(Math.Abs((phi - pi) / pi), exp) / div;
            return new VectorD(scaleX * r * Math.Cos(phi), scaleY * r * Math.Sin(phi), v.Z);
        }
        public VectorD Inverse(VectorD v)
        {
            double x = v.X / scaleX;
            double y = v.Y / scaleY;
            const double pi = Math.PI;
            double r = Math.Sqrt(x * x + y * y);
            double phi = Math.Acos(x / r);

            r /= 1 + Math.Pow(Math.Abs((phi - pi) / pi), exp) / div;
            if (y < 0) phi = (2 * pi) - phi;
            return new VectorD(r, phi, v.Z);
        }

        public bool SetScale(double x, double y)
        {
            if (x > 0 && y > 0)
            {
                scaleX = x; scaleY = y;
                return true;
            }
            return false;
        }
        public bool SetParam(double x, double y)
        {
            bool ret = false;
            if (x > 0)
            {
                exp = x;
                ret = true;
            }
            if (y > 0)
            {
                div = y;
                ret = true;
            }
            return ret;
        }

    }


    public class MarquiseTransform : CoordinateTransform
    {
        double scaleX = 1, scaleY = 1;
        double exp = 3;
        double div = 3;
        public VectorD Transform(VectorD v)
        {
            double r = v.X;
            double phi = v.Y;
            const double pi = Math.PI;
            r *= 1 + Math.Pow(Math.Abs((Math.Abs(phi-pi) - pi/2)*2 / pi), exp) / div;
            return new VectorD(scaleX * r * Math.Cos(phi), scaleY * r * Math.Sin(phi), v.Z);
        }
        public VectorD Inverse(VectorD v)
        {
            double x = v.X / scaleX;
            double y = v.Y / scaleY;
            const double pi = Math.PI;
            double r = Math.Sqrt(x * x + y * y);
            double phi = Math.Acos(x / r);
            
            r /= 1 + Math.Pow(Math.Abs((Math.Abs(phi-pi) - pi/2)*2 / pi), exp) / div;
            if (y < 0) phi = (2 * pi) - phi;
            return new VectorD(r, phi, v.Z);
        }

        public bool SetScale(double x, double y)
        {
            if (x > 0 && y > 0)
            {
                scaleX = x; scaleY = y;
                return true;
            }
            return false;
        }
        public bool SetParam(double x, double y)
        {
            bool ret = false;
            if (x > 0)
            {
                exp = x;
                ret = true;
            }
            if (y > 0)
            {
                div = y;
                ret = true;
            }
            return ret;
        }

    }
  

}